<template>
  <Teleport :to>
    <div class="rectangle" ref="rectangle"></div>
  </Teleport>
</template>

<script setup lang='ts'>
const rectangle = ref<HTMLElement>()
const props = defineProps<{
  elements: HTMLElement[] | undefined
  to: HTMLElement,
  disabled?: boolean,
}>()
const { elements, to, disabled } = toRefs(props)
function rectangleIntersectJudge(dom1: HTMLElement, dom2: HTMLElement) {
  const rect1 = dom1.getBoundingClientRect();
  const rect2 = dom2.getBoundingClientRect();
  return !(
    rect1.top > rect2.bottom ||
    rect1.right < rect2.left ||
    rect1.bottom < rect2.top ||
    rect1.left > rect2.right
  );
}
const emits = defineEmits(['select', 'confirm'])
const selected: HTMLElement[] = []
onMounted(() => {
  init()
})
const init = () => {
  let mouseOn = false;
  let startX = 0;
  let startY = 0;
  // 获取容器元素 
  to.value.onmousedown = function (e) {
    if (elements.value?.some(item => item.contains(e.target as HTMLElement))) return
    if (elements.value?.includes(e.target as HTMLElement)) return
    if (!to.value.contains(e.target as HTMLElement)) return
    if (e.buttons !== 1 || e.which !== 1) return;
    document.body.style.userSelect = "none";
    setTimeout(function () {
      mouseOn = true;
      startX = e.clientX - to.value.offsetLeft + to.value.scrollLeft;
      startY = e.clientY - to.value.offsetTop + to.value.scrollTop;
      rectangle.value!.style.left = startX + 'px';
      rectangle.value!.style.top = startY + 'px';
    }, 20);
  }
  document.onmousemove = function (e) {
    if (!mouseOn) return;
    const toRect = to.value.getBoundingClientRect()
    const _x = e.clientX - to.value.offsetLeft + to.value.scrollLeft;
    const _y = e.clientY - to.value.offsetTop + to.value.scrollTop;
    // 向上拖拽
    if (e.clientY < to.value.offsetTop) {
      let minTop = 0
      let step = to.value.scrollTop - 20
      if (step <= minTop) {
        to.value.scrollTop = minTop
      } else {
        to.value.scrollTop = step
      }
    }
    // 向下拖拽
    if (e.clientY > to.value.offsetTop + to.value.offsetHeight) {
      let maxTop = to.value.scrollHeight - to.value.offsetHeight
      let step = to.value.scrollTop + 20
      if (step >= maxTop) {
        to.value.scrollTop = maxTop
      } else {
        to.value.scrollTop = step
      }
    }
    rectangle.value!.style.display = 'block';
    rectangle.value!.style.left = Math.min(_x, startX) - toRect.left + 'px';
    rectangle.value!.style.top = Math.min(_y, startY) + 'px';
    if ((Math.min(_x, startX) + Math.abs(_x - startX)) <= to.value.scrollWidth) {
      rectangle.value!.style.width = Math.abs(_x - startX) + 'px';
    }
    if (to.value.scrollWidth === to.value.clientWidth) {
      rectangle.value!.style.width = Math.abs(_x - startX) + 'px';
    }
    if ((Math.min(_y, startY) + Math.abs(_y - startY)) <= to.value.scrollHeight) {
      rectangle.value!.style.height = Math.abs(_y - startY) + 'px';
    }
    if (to.value.scrollHeight === to.value.clientHeight) {
      rectangle.value!.style.height = Math.abs(_y - startY) + 'px';
    }
    const rectangleComputedStyle = rectangle.value!.getBoundingClientRect()
    elements.value?.forEach(item => {
      if (rectangleIntersectJudge(item, rectangle.value!)) {
        if (!selected!.includes(item)) {
          if (rectangleComputedStyle.width > 0 &&
            rectangleComputedStyle.height > 0) {
            selected!.push(item);
          }
        }
      } else {
        if (selected!.includes(item)) {
          const index = selected!.indexOf(item);
          selected!.splice(index, 1);
        }
      }
    })
    emits('select', selected)
  };
  document.onmouseup = function () {
    if (!mouseOn) return;
    document.body.style.userSelect = 'auto'
    emits('confirm', selected)
    rectangle.value!.style.display = 'none';
    mouseOn = false;
  };
}
watch(() => disabled.value, () => {
  if (disabled.value) {
    to.value.onmousedown = null;
    document.onmousemove = null;
    document.onmouseup = null;
  } else {
    init()
  }
})
</script>

<style lang="scss" scoped>
.rectangle {
  position: absolute;
  top: 0;
  left: 0;
  background-color: #6688f04f;
}
</style>